﻿using LockheedMartin.Prepar3D.SimConnect;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Interop;

namespace SimWithP3d.Vehicle
{
    public class MotionPlatform
    {
        public static SimConnect P3DMoudle = null;
        public static IntPtr Handle { get; private set; }
        public static HwndSource HandleSource { get; private set; }
        public const int WM_USER_SIMCONNECT = 0x0402;
        static Thread athread = null;

        enum DEFINITIONS
        {
            Struct1,
        }

        enum DATA_REQUESTS
        {
            REQUEST_1,
        }
        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
        public struct Struct1
        {
            public double pitch_;
            public double bank_;
            public double heading_;

            public double pitch_Acc;
            public double bank_Acc;
            public double yaw_Acc;

            public double x_Acc;
            public double y_Acc;
            public double z_Acc;

            public double pitch_Vel;
            public double bank_Vel;
            public double yaw_Vel;
        };

        public MotionPlatform(Window window)
        {
            P3DMoudle = null;
            //Create an event handle for the WPF window to listen for SimConnect events
            Handle = new WindowInteropHelper(window).Handle; // Get handle of main WPF Window
            HandleSource = HwndSource.FromHwnd(Handle); // Get source of handle in order to add event handlers to it
            HandleSource.AddHook(HandleSimConnectEvents);

        }
        ~MotionPlatform()
        {
            if (HandleSource != null)
            {
                HandleSource.RemoveHook(HandleSimConnectEvents);
            }
        }

        public static void Connect()
        {
            if (P3DMoudle == null)
            {
                try
                {
                    Thread.Sleep(1000);
                    // the constructor is similar to SimConnect_Open in the native API
                    P3DMoudle = new SimConnect("P3d Connect", Handle, WM_USER_SIMCONNECT, null, 0);

                    SetupEventHandlers();
                    Console.WriteLine("Success!");

                }
                catch (COMException ex)
                {
                    if (P3DMoudle != null)
                        P3DMoudle.Dispose();
                    P3DMoudle = null;
                    Console.WriteLine("Connect => Unable to connect to Prepar3D:\n" + ex.Message);
                    if (athread == null)
                    {
                        athread = new Thread(RequestData);
                        athread.IsBackground = true;
                        athread.Start();
                    }
                }
            }
            else
            {
                Console.WriteLine("already cononected!!!");
            }
        }
        public static void Simconnect_OnRecvOpen(SimConnect sender, SIMCONNECT_RECV_OPEN data)
        {
            Console.WriteLine("Connected to Prepar3D");
            if (athread == null)
            {
                athread = new Thread(RequestData);
                athread.IsBackground = true;
                athread.Start();
            }
        }
        public static void Disconnect()
        {
            //requestDataTimer.IsEnabled = false;

            if (P3DMoudle != null)
            {
                //Dispose serves the same purpose as SimConnect_Close()
                P3DMoudle.Dispose();
                P3DMoudle = null;

                Console.WriteLine("Connection closed");
            }
            Thread.Sleep(10000);
        }
        public static void SetupEventHandlers()
        {
            try
            {
                // listen to connect and quit msgs
                P3DMoudle.OnRecvOpen += new SimConnect.RecvOpenEventHandler(Simconnect_OnRecvOpen);
                P3DMoudle.OnRecvQuit += new SimConnect.RecvQuitEventHandler(Simconnect_OnRecvQuit);

                // listen to exceptions
                P3DMoudle.OnRecvException += new SimConnect.RecvExceptionEventHandler(Simconnect_OnRecvException);



                P3DMoudle.AddToDataDefinition(DEFINITIONS.Struct1, "PLANE PITCH DEGREES", "radians", SIMCONNECT_DATATYPE.FLOAT64, 0.0f, SimConnect.SIMCONNECT_UNUSED);
                P3DMoudle.AddToDataDefinition(DEFINITIONS.Struct1, "PLANE BANK DEGREES", "radians", SIMCONNECT_DATATYPE.FLOAT64, 0.0f, SimConnect.SIMCONNECT_UNUSED);
                P3DMoudle.AddToDataDefinition(DEFINITIONS.Struct1, "PLANE HEADING DEGREES TRUE", "radians", SIMCONNECT_DATATYPE.FLOAT64, 0.0f, SimConnect.SIMCONNECT_UNUSED);

                P3DMoudle.AddToDataDefinition(DEFINITIONS.Struct1, "ROTATION ACCELERATION BODY X", "radians per second", SIMCONNECT_DATATYPE.FLOAT64, 0.0f, SimConnect.SIMCONNECT_UNUSED);
                P3DMoudle.AddToDataDefinition(DEFINITIONS.Struct1, "ROTATION ACCELERATION BODY Y", "radians per second", SIMCONNECT_DATATYPE.FLOAT64, 0.0f, SimConnect.SIMCONNECT_UNUSED);
                P3DMoudle.AddToDataDefinition(DEFINITIONS.Struct1, "ROTATION ACCELERATION BODY Z", "radians per second", SIMCONNECT_DATATYPE.FLOAT64, 0.0f, SimConnect.SIMCONNECT_UNUSED);

                P3DMoudle.AddToDataDefinition(DEFINITIONS.Struct1, "ACCELERATION BODY X", "feet per second squared", SIMCONNECT_DATATYPE.FLOAT64, 0.0f, SimConnect.SIMCONNECT_UNUSED);
                P3DMoudle.AddToDataDefinition(DEFINITIONS.Struct1, "ACCELERATION BODY Y", "feet per second squared", SIMCONNECT_DATATYPE.FLOAT64, 0.0f, SimConnect.SIMCONNECT_UNUSED);
                P3DMoudle.AddToDataDefinition(DEFINITIONS.Struct1, "ACCELERATION BODY Z", "feet per second squared", SIMCONNECT_DATATYPE.FLOAT64, 0.0f, SimConnect.SIMCONNECT_UNUSED);

                P3DMoudle.AddToDataDefinition(DEFINITIONS.Struct1, "ROTATION VELOCITY BODY X", "feet per second", SIMCONNECT_DATATYPE.FLOAT64, 0.0f, SimConnect.SIMCONNECT_UNUSED);
                P3DMoudle.AddToDataDefinition(DEFINITIONS.Struct1, "ROTATION VELOCITY BODY Y", "feet per second", SIMCONNECT_DATATYPE.FLOAT64, 0.0f, SimConnect.SIMCONNECT_UNUSED);
                P3DMoudle.AddToDataDefinition(DEFINITIONS.Struct1, "ROTATION VELOCITY BODY Z", "feet per second", SIMCONNECT_DATATYPE.FLOAT64, 0.0f, SimConnect.SIMCONNECT_UNUSED);



                P3DMoudle.RegisterDataDefineStruct<Struct1>(DEFINITIONS.Struct1);


                P3DMoudle.OnRecvSimobjectDataBytype += new SimConnect.RecvSimobjectDataBytypeEventHandler(simconnect_OnRecvSimobjectDataBytype);


                P3DMoudle.WeatherSetModeGlobal();
            }
            catch (Exception ex)
            {
                P3DMoudle.Dispose();
                P3DMoudle = null;

            }
        }

        /// The case where the user closes Prepar3D
        public static void Simconnect_OnRecvQuit(SimConnect sender, SIMCONNECT_RECV data)
        {

            Console.WriteLine("Prepar3D has exited");
            if (P3DMoudle != null)
            {
                P3DMoudle.Dispose();
                P3DMoudle = null;
            }
        }

        public static void Simconnect_OnRecvException(SimConnect sender, SIMCONNECT_RECV_EXCEPTION data)
        {
            Console.WriteLine("Exception received: " + data.dwException);
        }
        public static double pitch_;
        public static double bank_;
        public static double heading_;

        public static double pitch_Acc;
        public static double bank_Acc;
        public static double yaw_Acc;

        public static double x_Acc;
        public static double y_Acc;
        public static double z_Acc;

        public static double pitch_Vel;
        public static double roll_Vel;
        public static double yaw_Vel;

        public static void simconnect_OnRecvSimobjectDataBytype(SimConnect sender, SIMCONNECT_RECV_SIMOBJECT_DATA_BYTYPE data)
        {
            try
            {
                switch ((DATA_REQUESTS)data.dwRequestID)
                {
                    case DATA_REQUESTS.REQUEST_1:
                        Struct1 s1 = (Struct1)data.dwData[0];
                        pitch_ = s1.pitch_;
                        bank_ = s1.bank_;
                        heading_ = s1.heading_;

                        pitch_Acc = s1.pitch_Acc;
                        bank_Acc = s1.bank_Acc;
                        yaw_Acc = s1.yaw_Acc;

                        x_Acc = s1.x_Acc * 0.3048;//英尺到米
                        y_Acc = s1.y_Acc * 0.3048;//英尺到米
                        z_Acc = s1.z_Acc * 0.3048;//英尺到米

                        pitch_Vel = s1.pitch_Vel * 0.3048;//英尺到米
                        roll_Vel = s1.bank_Vel * 0.3048;//英尺到米
                        yaw_Vel = s1.yaw_Vel * 0.3048;//英尺到米

                        break;
                    default:
                        break;
                }
            }
            catch (Exception ex)
            {
                App.RecordErrorLog(ex);
                Thread.Sleep(5000);
            }
        }
        public static void RequestData()
        {
            while (true)
            {
                try
                {
                    if (P3DMoudle != null)
                    {
                        P3DMoudle.RequestDataOnSimObjectType(DATA_REQUESTS.REQUEST_1, DEFINITIONS.Struct1, 0, SIMCONNECT_SIMOBJECT_TYPE.USER);
                        Thread.Sleep(40);
                    }
                    else
                    {
                        P3DMoudle = null;
                        Thread.Sleep(15000);
                        Connect();
                        Thread.Sleep(500);
                        Console.WriteLine("在重连simmconnect 385");
                    }
                }
                catch (Exception ex)
                {
                    if (P3DMoudle != null)
                        //    P3DMoudle.Dispose();
                        P3DMoudle = null;
                    Thread.Sleep(15000);
                    Connect();
                    Thread.Sleep(500);
                    Console.WriteLine("在重连simmconnect 397");
                }
            }
        }

        [HandleProcessCorruptedStateExceptions]//处理捕获的C++异常
        [SecurityCritical]
        public static IntPtr HandleSimConnectEvents(IntPtr hWnd, int message, IntPtr wParam, IntPtr lParam, ref bool isHandled)
        {
            try
            {
                isHandled = false;
                switch (message)
                {
                    case WM_USER_SIMCONNECT:
                        {
                            if (P3DMoudle != null)
                            {
                                try
                                {
                                    P3DMoudle.ReceiveMessage();
                                }
                                catch
                                {
                                    RecoverFromError();
                                }

                                isHandled = true;
                            }
                            break;
                        }
                    default:
                        break;
                }
            }
            catch
            {
                if (P3DMoudle != null)
                    P3DMoudle.Dispose();
                P3DMoudle = null;
            }
            return IntPtr.Zero;
        }



        public static void RecoverFromError()
        {
            Disconnect();
            Connect();
        }
    }
}
